/* Copyright (c) 2010, mitsuhisa.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of the misuhisa.net nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <cstdlib>
#include "NyuTree.hpp"
#include "NyuContext.hpp"
#include "NyuObject.hpp"
#include "NyuString.hpp"
#include "NyuStrPool.hpp"
#include "NyuSymtbl.hpp"
#include "NyuRange.hpp"
#include "NyuArray.hpp"
#include "NyuError.hpp"

NyuTree::~NyuTree() {
}

//void* NyuTree::operator new(size_t size) {
//    return ::malloc(size);
//}

//void NyuTree::operator delete(void* p) {
//    ::free(p);
//}

NyuValue NyuTree::eval_put(NyuValue v2, NyuContext* c, bool is_tail) {
    // DUMMY
    return 0;
}

NyuValue NyuTree::eval_set(NyuValue v2, NyuContext* c, bool is_tail) {
    // DUMMY
    return 0;
}

NyuValue NyuTree::eval_iadd(NyuValue v2, NyuContext* c, bool is_tail) {
    // DUMMY
    return 0;
}

NyuValue NyuTree::eval_isub(NyuValue v2, NyuContext* c, bool is_tail) {
    // DUMMY
    return 0;
}

NyuTreeList::NyuTreeList(NyuTree* value, NyuTreeList* next) :
        value_(value), next_(next) {
}

NyuTreeList::~NyuTreeList() {
    delete value_;
    delete next_;
}

NyuValue NyuTreeList::eval(NyuContext* c, bool is_tail) {
    NyuTreeList* l = this;

    if (!l)
        return 0;

    for (; l->next_; l = l->next_) {
        l->value_->eval(c);
    }

    return l->value_->eval(c, true);
}

NyuTreeUnit::NyuTreeUnit(NyuTreeList* stmts) : stmts_(stmts) {
}

NyuTreeUnit::~NyuTreeUnit() {
    delete stmts_;
}

NyuValue NyuTreeUnit::eval(NyuContext* c, bool is_tail) {
    NyuTreeList* l = stmts_;

    if (!l)
        return 0;

    for (; l->next(); l = l->next()) {
        l->value()->eval(c);
    }

    return l->value()->eval(c);
}

NyuTreeExpr1::NyuTreeExpr1(NyuTree* node) : node_(node) {
}

NyuTreeExpr1::~NyuTreeExpr1() {
    delete node_;
}

#define NYU_TREE_OP_Not(X) NYU_INT_TO_VALUE(!(X))
#define NYU_TREE_OP_Invert(X) NYU_VALUE_CLEAR_TAG(~(X))
#define NYU_TREE_OP_Pos +
#define NYU_TREE_OP_Neg -
#define NYU_TREE_OP_Incr(X) ((X) += NYU_INT_TO_VALUE(1))
#define NYU_TREE_OP_Decr(X) ((X) -= NYU_INT_TO_VALUE(1))
#define NYU_TREE_IMPL_EXPR1_1(NAME,OP)                            \
NyuValue NyuTree##NAME::eval(NyuContext* c, bool is_tail) {       \
    NyuValue v1 = node_->eval(c);                                 \
                                                                  \
    switch (NYU_VALUE_TAG(v1)) {                                  \
    case NYU_VALUE_TAG_INT:                                       \
        return NYU_TREE_OP_##NAME(v1);                            \
                                                                  \
    case NYU_VALUE_TAG_NATIVE:                                    \
        std::cerr << "bad expr " #OP "(native)" << std::endl;     \
        return 0;                                                 \
                                                                  \
    case NYU_VALUE_TAG_OBJECT:                                    \
        return NYU_VALUE_UNTAG_OBJECT(v1)->op##NAME(c);           \
                                                                  \
    case NYU_VALUE_TAG_BLOCK:                                     \
        std::cerr << "bad expr " #OP "(block)" << std::endl;      \
        return 0;                                                 \
    }                                                             \
                                                                  \
    return 0;                                                     \
}

NYU_TREE_IMPL_EXPR1_1(Not,!);
NYU_TREE_IMPL_EXPR1_1(Invert,~);
NYU_TREE_IMPL_EXPR1_1(Pos,+);
NYU_TREE_IMPL_EXPR1_1(Neg,-);
NYU_TREE_IMPL_EXPR1_1(Incr,++);
NYU_TREE_IMPL_EXPR1_1(Decr,--);

#undef NYU_TREE_IMPL_EXPR1_1
#undef NYU_TREE_OP_Not
#undef NYU_TREE_OP_Invert
#undef NYU_TREE_OP_Pos
#undef NYU_TREE_OP_Neg
#undef NYU_TREE_OP_Incr
#undef NYU_TREE_OP_Decr

#define NYU_TREE_IMPL_EXPR1_2(NAME,OP,OP2)                        \
NyuValue NyuTree##NAME::eval(NyuContext* c, bool is_tail) {       \
    NyuValue v1 = node_->eval(c);                                 \
    NyuValue v2;                                                  \
                                                                  \
    switch (NYU_VALUE_TAG(v1)) {                                  \
    case NYU_VALUE_TAG_INT:                                       \
        v2 = v1;                                                  \
        v1 OP2 2;                                                 \
        return v2;                                                \
                                                                  \
    case NYU_VALUE_TAG_NATIVE:                                    \
        std::cerr << "bad expr (native)" #OP << std::endl;        \
        return 0;                                                 \
                                                                  \
    case NYU_VALUE_TAG_OBJECT:                                    \
        return NYU_VALUE_UNTAG_OBJECT(v1)->op##NAME(c);           \
                                                                  \
    case NYU_VALUE_TAG_BLOCK:                                     \
        std::cerr << "bad expr (block)" #OP << std::endl;         \
        return 0;                                                 \
    }                                                             \
                                                                  \
    return 0;                                                     \
}

NYU_TREE_IMPL_EXPR1_2(PostIncr,++,+=);
NYU_TREE_IMPL_EXPR1_2(PostDecr,--,-=);

#undef NYU_TREE_IMPL_EXPR1_2

NyuTreeExpr2::NyuTreeExpr2(NyuTree* lnode, NyuTree* rnode) :
        lnode_(lnode), rnode_(rnode) {
}

NyuTreeExpr2::~NyuTreeExpr2() {
    delete lnode_;
    delete rnode_;
}

#define NYU_TREE_IMPL_EXPR2_1(NAME,OP)                                  \
NyuValue NyuTree##NAME::eval(NyuContext* c, bool is_tail) {             \
    NyuValue v1 = lnode_->eval(c);                                      \
    NyuValue v2 = rnode_->eval(c);                                      \
                                                                        \
    switch (NYU_VALUE_TAG2(v1, v2)) {                                   \
    case NYU_VALUE_TAG_INT_INT:                                         \
        return v1 OP v2;                                                \
                                                                        \
    case NYU_VALUE_TAG_INT_NATIVE:                                      \
        std::cerr << "bad expr int " #OP " native" << std::endl;        \
        return 0;                                                       \
                                                                        \
    case NYU_VALUE_TAG_INT_OBJECT:                                      \
        return v1 + NYU_VALUE_UNTAG_OBJECT(v2)->to_intval(c);           \
                                                                        \
    case NYU_VALUE_TAG_INT_BLOCK:                                       \
        std::cerr << "bad expr int " #OP " block" << std::endl;         \
        return 0;                                                       \
                                                                        \
    case NYU_VALUE_TAG_NATIVE_INT:                                      \
    case NYU_VALUE_TAG_NATIVE_NATIVE:                                   \
    case NYU_VALUE_TAG_NATIVE_OBJECT:                                   \
    case NYU_VALUE_TAG_NATIVE_BLOCK:                                    \
        std::cerr << "bad expr native " #OP " ???" << std::endl;        \
        return 0;                                                       \
                                                                        \
    case NYU_VALUE_TAG_OBJECT_INT:                                      \
    case NYU_VALUE_TAG_OBJECT_NATIVE:                                   \
    case NYU_VALUE_TAG_OBJECT_OBJECT:                                   \
    case NYU_VALUE_TAG_OBJECT_BLOCK:                                    \
        c->setCurrentTarget(v1);                                        \
        return NYU_VALUE_UNTAG_OBJECT(v1)->op##NAME(v2, c, is_tail);    \
                                                                        \
    case NYU_VALUE_TAG_BLOCK_INT:                                       \
    case NYU_VALUE_TAG_BLOCK_NATIVE:                                    \
    case NYU_VALUE_TAG_BLOCK_OBJECT:                                    \
    case NYU_VALUE_TAG_BLOCK_BLOCK:                                     \
        std::cerr << "bad expr block " #OP " ???" << std::endl;         \
        return 0;                                                       \
    }                                                                   \
                                                                        \
    return 0;                                                           \
}

NYU_TREE_IMPL_EXPR2_1(Add,+);
NYU_TREE_IMPL_EXPR2_1(Sub,-);
NYU_TREE_IMPL_EXPR2_1(Mod,%);
NYU_TREE_IMPL_EXPR2_1(Or,|);
NYU_TREE_IMPL_EXPR2_1(Xor,^);
NYU_TREE_IMPL_EXPR2_1(And,&);

#undef NYU_TREE_IMPL_EXPR2_1

#define NYU_TREE_IMPL_EXPR2_2(NAME,OP)                                  \
NyuValue NyuTree##NAME::eval(NyuContext* c, bool is_tail) {             \
    NyuValue v1 = lnode_->eval(c);                                      \
    NyuValue v2 = rnode_->eval(c);                                      \
                                                                        \
    switch (NYU_VALUE_TAG2(v1, v2)) {                                   \
    case NYU_VALUE_TAG_INT_INT:                                         \
        return v1 OP NYU_VALUE_TO_INT(v2);                              \
                                                                        \
    case NYU_VALUE_TAG_INT_NATIVE:                                      \
        std::cerr << "bad expr int " #OP " native" << std::endl;        \
        return 0;                                                       \
                                                                        \
    case NYU_VALUE_TAG_INT_OBJECT:                                      \
        return v1 + NYU_VALUE_UNTAG_OBJECT(v2)->to_int(c);              \
                                                                        \
    case NYU_VALUE_TAG_INT_BLOCK:                                       \
        std::cerr << "bad expr int " #OP " block" << std::endl;         \
        return 0;                                                       \
                                                                        \
    case NYU_VALUE_TAG_NATIVE_INT:                                      \
    case NYU_VALUE_TAG_NATIVE_NATIVE:                                   \
    case NYU_VALUE_TAG_NATIVE_OBJECT:                                   \
    case NYU_VALUE_TAG_NATIVE_BLOCK:                                    \
        std::cerr << "bad expr native " #OP " ???" << std::endl;        \
        return 0;                                                       \
                                                                        \
    case NYU_VALUE_TAG_OBJECT_INT:                                      \
    case NYU_VALUE_TAG_OBJECT_NATIVE:                                   \
    case NYU_VALUE_TAG_OBJECT_OBJECT:                                   \
    case NYU_VALUE_TAG_OBJECT_BLOCK:                                    \
        c->setCurrentTarget(v1);                                        \
        return NYU_VALUE_UNTAG_OBJECT(v1)->op##NAME(v2, c, is_tail);    \
                                                                        \
    case NYU_VALUE_TAG_BLOCK_INT:                                       \
    case NYU_VALUE_TAG_BLOCK_NATIVE:                                    \
    case NYU_VALUE_TAG_BLOCK_OBJECT:                                    \
    case NYU_VALUE_TAG_BLOCK_BLOCK:                                     \
        std::cerr << "bad expr block " #OP " ???" << std::endl;         \
        return 0;                                                       \
    }                                                                   \
                                                                        \
    return 0;                                                           \
}

NYU_TREE_IMPL_EXPR2_2(Mul,*);
NYU_TREE_IMPL_EXPR2_2(Div,/);
NYU_TREE_IMPL_EXPR2_2(LShift,<<);

#undef NYU_TREE_IMPL_EXPR2_2

#define NYU_TREE_IMPL_EXPR2_3(NAME,OP)                                        \
NyuValue NyuTree##NAME::eval(NyuContext* c, bool is_tail) {                   \
    NyuValue v1 = lnode_->eval(c);                                            \
    NyuValue v2 = rnode_->eval(c);                                            \
                                                                              \
    switch (NYU_VALUE_TAG2(v1, v2)) {                                         \
    case NYU_VALUE_TAG_INT_INT:                                               \
        return NYU_INT_TO_VALUE(v1 OP v2);                                    \
                                                                              \
    case NYU_VALUE_TAG_INT_NATIVE:                                            \
        std::cerr << "bad expr int " #OP " native" << std::endl;              \
        return 0;                                                             \
                                                                              \
    case NYU_VALUE_TAG_INT_OBJECT:                                            \
        return NYU_INT_TO_VALUE(v1 OP NYU_VALUE_UNTAG_OBJECT(v2)->to_int(c)); \
                                                                              \
    case NYU_VALUE_TAG_INT_BLOCK:                                             \
        std::cerr << "bad expr int " #OP " block" << std::endl;               \
        return 0;                                                             \
                                                                              \
    case NYU_VALUE_TAG_NATIVE_INT:                                            \
    case NYU_VALUE_TAG_NATIVE_NATIVE:                                         \
    case NYU_VALUE_TAG_NATIVE_OBJECT:                                         \
    case NYU_VALUE_TAG_NATIVE_BLOCK:                                          \
        std::cerr << "bad expr native " #OP " ???" << std::endl;              \
        return 0;                                                             \
                                                                              \
    case NYU_VALUE_TAG_OBJECT_INT:                                            \
    case NYU_VALUE_TAG_OBJECT_NATIVE:                                         \
    case NYU_VALUE_TAG_OBJECT_OBJECT:                                         \
    case NYU_VALUE_TAG_OBJECT_BLOCK:                                          \
        c->setCurrentTarget(v1);                                              \
        return NYU_VALUE_UNTAG_OBJECT(v1)->op##NAME(v2, c, is_tail);          \
                                                                              \
    case NYU_VALUE_TAG_BLOCK_INT:                                             \
    case NYU_VALUE_TAG_BLOCK_NATIVE:                                          \
    case NYU_VALUE_TAG_BLOCK_OBJECT:                                          \
    case NYU_VALUE_TAG_BLOCK_BLOCK:                                           \
        std::cerr << "bad expr block " #OP " ???" << std::endl;               \
        return 0;                                                             \
    }                                                                         \
                                                                              \
    return 0;                                                                 \
}

NYU_TREE_IMPL_EXPR2_3(Lt,<);
NYU_TREE_IMPL_EXPR2_3(Le,<=);
NYU_TREE_IMPL_EXPR2_3(Gt,>);
NYU_TREE_IMPL_EXPR2_3(Ge,>=);
NYU_TREE_IMPL_EXPR2_3(Eq,==);
NYU_TREE_IMPL_EXPR2_3(Ne,!=);

#undef NYU_TREE_IMPL_EXPR2_3

NyuValue NyuTreeCmp::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeRShift::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeLOr::eval(NyuContext* c, bool is_tail) {
    NyuValue v1 = lnode_->eval(c);
    return v1 ? v1 : rnode_->eval(c);
}

NyuValue NyuTreeLXor::eval(NyuContext* c, bool is_tail) {
    NyuValue v1 = lnode_->eval(c);
    NyuValue v2 = rnode_->eval(c);
    return v1 ? (v2 ? 0 : v1) : (v2 ? v2 : 0);
}

NyuValue NyuTreeLAnd::eval(NyuContext* c, bool is_tail) {
    NyuValue v1 = lnode_->eval(c);
    return v1 ? rnode_->eval(c) : v1;
}

NyuValue NyuTreeOwnProto::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeOwnMember::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreePut::eval(NyuContext* c, bool is_tail) {
    NyuValue v2 = rnode_->eval(c);
    return lnode_->eval_put(v2, c, is_tail);
}

NyuValue NyuTreeSet::eval(NyuContext* c, bool is_tail) {
    NyuValue v2 = rnode_->eval(c);
    return lnode_->eval_set(v2, c, is_tail);
}

NyuValue NyuTreeIAdd::eval(NyuContext* c, bool is_tail) {
    NyuValue v2 = rnode_->eval(c);
    return lnode_->eval_iadd(v2, c, is_tail);
}

NyuValue NyuTreeISub::eval(NyuContext* c, bool is_tail) {
    NyuValue v2 = rnode_->eval(c);
    return lnode_->eval_isub(v2, c, is_tail);
}

NyuValue NyuTreeIMul::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeIDiv::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeIMod::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeIAnd::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeIXor::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeIOr::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeILShift::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeIRShift::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeILAnd::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuValue NyuTreeILOr::eval(NyuContext* c, bool is_tail) {
    return 0;
}

NyuTreeCond::NyuTreeCond(NyuTree* cond, NyuTree* tnode, NyuTree* fnode) :
        cond_(cond), tnode_(tnode), fnode_(fnode) {
}

NyuTreeCond::~NyuTreeCond() {
    delete cond_;
    delete tnode_;
    delete fnode_;
}

NyuValue NyuTreeCond::eval(NyuContext* c, bool is_tail) {
    NyuValue v1 = cond_->eval(c);

    if (NYU_VALUE_IS_OBJECT(v1)) {
        v1 = NYU_VALUE_UNTAG_OBJECT(v1)->to_int(c);
    }

    return (v1 ? tnode_ : fnode_)->eval(c, is_tail);
}

NyuTreeInt::NyuTreeInt(NyuInt value) :
        value_(NYU_INT_TO_VALUE(value)) {
}

NyuValue NyuTreeInt::eval(NyuContext* c, bool is_tail) {
    return value_;
}

NyuTreeFloat::NyuTreeFloat(NyuFloat value) :
        value_(0) {
}

NyuValue NyuTreeFloat::eval(NyuContext* c, bool is_tail) {
    return value_;
}

NyuTreeStr::NyuTreeStr(char* value) :
        value_(NYU_OBJECT_TO_VALUE(NyuStrPool::get(value))) {
}

NyuValue NyuTreeStr::eval(NyuContext* c, bool is_tail) {
    return value_;
}

NyuTreeSym::NyuTreeSym(char* value) :
        symbol_(NyuStrPool::get(value)),
        symidx_(NyuSymtbl::getIndex(symbol_)) {
}

NyuValue NyuTreeSym::eval(NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(symidx_);
    return ref ? ref->value : 0;
}

NyuValue NyuTreeSym::eval_put(NyuValue v2, NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(symidx_);
    if (ref && ref->scope == c->current_scope()) {
        ref->value = v2;
    } else {
        ref = c->put_symref(symidx_, v2);
    }
    return ref->value;
}

NyuValue NyuTreeSym::eval_set(NyuValue v2, NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(symidx_);
    if (ref) {
        ref->value = v2;
    } else {
        ref = c->put_symref(symidx_, v2);
    }
    return ref->value;
}

NyuValue NyuTreeSym::eval_iadd(NyuValue v2, NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(symidx_);
    if (ref) {
        NyuValue& v1 = ref->value;

        switch (NYU_VALUE_TAG2(v1, v2)) {
        case NYU_VALUE_TAG_INT_INT:
            return v1 += v2;

        case NYU_VALUE_TAG_INT_NATIVE:
            std::cerr << "bad expr int += native" << std::endl;
            return 0;

        case NYU_VALUE_TAG_INT_OBJECT:
            return v1 += NYU_VALUE_UNTAG_OBJECT(v2)->to_intval(c);

        case NYU_VALUE_TAG_INT_BLOCK:
            std::cerr << "bad expr int += block" << std::endl;
            return 0;

        case NYU_VALUE_TAG_NATIVE_INT:
        case NYU_VALUE_TAG_NATIVE_NATIVE:
        case NYU_VALUE_TAG_NATIVE_OBJECT:
        case NYU_VALUE_TAG_NATIVE_BLOCK:
            std::cerr << "bad expr native += ???" << std::endl;
            return 0;

        case NYU_VALUE_TAG_OBJECT_INT:
        case NYU_VALUE_TAG_OBJECT_NATIVE:
        case NYU_VALUE_TAG_OBJECT_OBJECT:
        case NYU_VALUE_TAG_OBJECT_BLOCK:
            return NYU_VALUE_UNTAG_OBJECT(v1)->opIAdd(v2, c, is_tail);

        case NYU_VALUE_TAG_BLOCK_INT:
        case NYU_VALUE_TAG_BLOCK_NATIVE:
        case NYU_VALUE_TAG_BLOCK_OBJECT:
        case NYU_VALUE_TAG_BLOCK_BLOCK:
            std::cerr << "bad expr block += ???" << std::endl;
            return 0;
        }
    } else {
        NyuError::doThrow("Variable not defined");
    }
    return ref->value;
}

NyuValue NyuTreeSym::eval_isub(NyuValue v2, NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(symidx_);
    if (ref) {
        NyuValue& v1 = ref->value;

        switch (NYU_VALUE_TAG2(v1, v2)) {
        case NYU_VALUE_TAG_INT_INT:
            return v1 -= v2;

        case NYU_VALUE_TAG_INT_NATIVE:
            std::cerr << "bad expr int -= native" << std::endl;
            return 0;

        case NYU_VALUE_TAG_INT_OBJECT:
            return v1 -= NYU_VALUE_UNTAG_OBJECT(v2)->to_intval(c);

        case NYU_VALUE_TAG_INT_BLOCK:
            std::cerr << "bad expr int -= block" << std::endl;
            return 0;

        case NYU_VALUE_TAG_NATIVE_INT:
        case NYU_VALUE_TAG_NATIVE_NATIVE:
        case NYU_VALUE_TAG_NATIVE_OBJECT:
        case NYU_VALUE_TAG_NATIVE_BLOCK:
            std::cerr << "bad expr native -= ???" << std::endl;
            return 0;

        case NYU_VALUE_TAG_OBJECT_INT:
        case NYU_VALUE_TAG_OBJECT_NATIVE:
        case NYU_VALUE_TAG_OBJECT_OBJECT:
        case NYU_VALUE_TAG_OBJECT_BLOCK:
            return NYU_VALUE_UNTAG_OBJECT(v1)->opISub(v2, c, is_tail);

        case NYU_VALUE_TAG_BLOCK_INT:
        case NYU_VALUE_TAG_BLOCK_NATIVE:
        case NYU_VALUE_TAG_BLOCK_OBJECT:
        case NYU_VALUE_TAG_BLOCK_BLOCK:
            std::cerr << "bad expr block -= ???" << std::endl;
            return 0;
        }
    } else {
        NyuError::doThrow("Variable not defined");
    }
    return ref->value;
}

NyuTreeBlock::NyuTreeBlock(NyuTreeList* stmts) : stmts_(stmts) {
}

NyuTreeBlock::~NyuTreeBlock() {
    delete stmts_;
}

NyuValue NyuTreeBlock::eval(NyuContext* c, bool is_tail) {
    return NYU_BLOCK_TO_VALUE(stmts_);
}

NyuTreeCurblk::NyuTreeCurblk() {
}

NyuValue NyuTreeCurblk::eval(NyuContext* c, bool is_tail) {
    return c->current_block();
}

NyuTreeArglen::NyuTreeArglen() {
}

NyuValue NyuTreeArglen::eval(NyuContext* c, bool is_tail) {
    return NYU_INT_TO_VALUE(c->current_argc());
}

NyuTreeArgref::NyuTreeArgref(int idx) : idx_(idx) {
}

NyuValue NyuTreeArgref::eval(NyuContext* c, bool is_tail) {
    if (idx_ < 0 || c->current_argc() <= idx_)
        return 0;
    return c->current_argv(idx_);
}

NyuTreeCall::NyuTreeCall(NyuTree* expr, NyuTreeList* args) :
        expr_(expr), args_(args) {
}

NyuTreeCall::~NyuTreeCall() {
    delete expr_;
    delete args_;
}

NyuValue NyuTreeCall::eval(NyuContext* c, bool is_tail) {
    NyuValue v1 = expr_->eval(c);
    NyuValue ret = 0;

    if (is_tail) {
        c->update_scope(v1, args_);
        return 0;
    }

    c->push_scope(v1, args_);

call_val:

    switch (NYU_VALUE_TAG(v1)) {
    case NYU_VALUE_TAG_INT:
        std::cerr << "int is not callable!" << std::endl;
        break;

    case NYU_VALUE_TAG_NATIVE:
        ret = NYU_VALUE_UNTAG_NATIVE(v1)(c);
        break;

    case NYU_VALUE_TAG_OBJECT:
        c->setCurrentTarget(v1);
        c->current_argv()[0] = v1;
        v1 = NYU_VALUE_UNTAG_OBJECT(v1)->getCall(c);
        goto call_val;

    case NYU_VALUE_TAG_BLOCK:
        {
            NyuTreeList* stmts = NYU_VALUE_UNTAG_BLOCK(v1);
            if (stmts)
                ret = stmts->eval(c);
        }
        if (c->is_scope_updated()) {
            v1 = c->current_block();
            c->set_is_scope_updated(false);
            goto call_val;
        }
        break;
    }

    c->pop_scope();

    return ret;
}

NyuTreeObject::NyuTreeObject(NyuTreeList* slots) :
        slots_(slots) {
}

NyuTreeObject::~NyuTreeObject() {
    delete slots_;
}

NyuValue NyuTreeObject::eval(NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(NyuSymtbl::getIndex(&NyuStaticString::OBJECT));

    NyuObject* proto = NYU_VALUE_UNTAG_OBJECT(ref->value);
    NyuObject* obj = new NyuObject(proto);

    for (NyuTreeList* l = slots_; l; l = l->next()) {
        NyuTreeObjectSlot* s = static_cast<NyuTreeObjectSlot*>(l->value());
        NyuValue v = s->value()->eval(c);
        obj->putSlot(s->name(), v, c);
    }

    return NYU_OBJECT_TO_VALUE(obj);
}

NyuTreeObjectSlot::NyuTreeObjectSlot(char* name, NyuTree* value) :
        name_(NyuStrPool::get(name)), value_(value) {
}

NyuTreeObjectSlot::~NyuTreeObjectSlot() {
    delete value_;
}

NyuValue NyuTreeObjectSlot::eval(NyuContext* c, bool is_tail) {
    // Nothing to do
    return 0;
}

NyuTreeArray::NyuTreeArray(NyuTreeList* elms) :
        elms_(elms) {
}

NyuTreeArray::~NyuTreeArray() {
    delete elms_;
}

NyuValue NyuTreeArray::eval(NyuContext* c, bool is_tail) {
    NyuSymref* ref = c->get_symref(NyuSymtbl::getIndex(&NyuStaticString::ARRAY));
    NyuObject* proto = NYU_VALUE_UNTAG_OBJECT(ref->value);

    size_t size = 0;
    for (NyuTreeList* l = elms_; l; l = l->next(), size++) {
    }

    NyuArray* array = new NyuArray(proto, size);
    size = 0;
    for (NyuTreeList* l = elms_; l; l = l->next(), size++) {
        array->directAt(size) = l->value()->eval(c);
    }

    return NYU_OBJECT_TO_VALUE(array);
}

NyuTreeRange::NyuTreeRange(NyuTree* start, NyuTree* end, NyuTree* step,
                           bool is_include_border) :
        start_(start), end_(end), step_(step),
        is_include_border_(is_include_border) {
}

NyuTreeRange::~NyuTreeRange() {
    delete start_;
    delete end_;
    delete step_;
}

NyuValue NyuTreeRange::eval(NyuContext* c, bool is_tail) {
    NyuValue start = start_->eval(c);
    NyuValue end = end_->eval(c);
    NyuValue step = 0;
    if (step_)
        step = step_->eval(c);

    NyuSymref* ref = c->get_symref(NyuSymtbl::getIndex(&NyuStaticString::RANGE));

    NyuObject* proto = NYU_VALUE_UNTAG_OBJECT(ref->value);

    return NYU_OBJECT_TO_VALUE(new NyuRange(proto, start, end, step, is_include_border_));
}

NyuTreeSlotAccess::NyuTreeSlotAccess(NyuTree* expr, char* name) :
        expr_(expr), name_(NyuStrPool::get(name)) {
}

NyuTreeSlotAccess::~NyuTreeSlotAccess() {
    delete expr_;
}

NyuValue NyuTreeSlotAccess::eval(NyuContext* c, bool is_tail) {
    NyuValue v1 = expr_->eval(c);

    c->setCurrentTarget(v1);

    switch (NYU_VALUE_TAG(v1)) {
    case NYU_VALUE_TAG_INT:
        std::cerr << "Sorry! currently int has no slots" << std::endl;
        return 0;

    case NYU_VALUE_TAG_NATIVE:
        std::cerr << "Sorry! currently native has no slots" << std::endl;
        return 0;

    case NYU_VALUE_TAG_OBJECT:
        return NYU_VALUE_UNTAG_OBJECT(v1)->getSlot(name_, c);

    case NYU_VALUE_TAG_BLOCK:
        std::cerr << "Sorry! currently block has no slots" << std::endl;
        return 0;
    }

    return 0;
}

NyuValue NyuTreeSlotAccess::eval_iadd(NyuValue v2, NyuContext* c, bool is_tail) {
    NyuValue v1 = expr_->eval(c);

    switch (NYU_VALUE_TAG(v1)) {
    case NYU_VALUE_TAG_INT:
        std::cerr << "Sorry! currently int has no slots" << std::endl;
        return 0;

    case NYU_VALUE_TAG_NATIVE:
        std::cerr << "Sorry! currently native has no slots" << std::endl;
        return 0;

    case NYU_VALUE_TAG_OBJECT:
        return NYU_VALUE_UNTAG_OBJECT(v1)->opIAddSlot(name_, v2, c, is_tail);

    case NYU_VALUE_TAG_BLOCK:
        std::cerr << "Sorry! currently block has no slots" << std::endl;
        return 0;
    }

    return 0;
}

NyuValue NyuTreeSlotAccess::eval_isub(NyuValue v2, NyuContext* c, bool is_tail) {
    NyuValue v1 = expr_->eval(c);

    switch (NYU_VALUE_TAG(v1)) {
    case NYU_VALUE_TAG_INT:
        std::cerr << "Sorry! currently int has no slots" << std::endl;
        return 0;

    case NYU_VALUE_TAG_NATIVE:
        std::cerr << "Sorry! currently native has no slots" << std::endl;
        return 0;

    case NYU_VALUE_TAG_OBJECT:
        return NYU_VALUE_UNTAG_OBJECT(v1)->opISubSlot(name_, v2, c, is_tail);

    case NYU_VALUE_TAG_BLOCK:
        std::cerr << "Sorry! currently block has no slots" << std::endl;
        return 0;
    }

    return 0;
}

NyuTreeGetItem::NyuTreeGetItem(NyuTree* expr, NyuTree* idx) :
        expr_(expr), idx_(idx) {
}

NyuTreeGetItem::~NyuTreeGetItem() {
    delete expr_;
    delete idx_;
}

NyuValue NyuTreeGetItem::eval(NyuContext* c, bool is_tail) {
    return 0;
}
